package com.ht.base.system.biz.manager.impl;

import com.ht.base.system.biz.convert.RouterVoBeanUtil;
import com.ht.base.system.biz.convert.SysMenuBeanUtil;
import com.ht.base.system.biz.manager.SelectMenuManager;
import com.ht.base.system.common.pojo.bo.SysMenuBO;
import com.ht.base.system.common.pojo.bo.SysUserBO;
import com.ht.base.system.common.pojo.qto.SysMenuQTO;
import com.ht.base.system.common.pojo.vo.MetaVo;
import com.ht.base.system.common.pojo.vo.RouterVo;
import com.ht.base.system.common.pojo.vo.TreeSelect;
import com.ht.base.system.dao.dao.SysMenuDao;
import com.ht.base.system.dao.dao.SysRoleDao;
import com.ht.base.system.dao.dao.SysRoleMenuDao;
import com.ht.base.system.dao.dao.SysUserRoleDao;
import com.ht.base.system.dao.domain.SysMenuDO;
import com.ht.base.system.dao.domain.SysRoleDO;
import com.ht.base.system.dao.domain.SysUserRoleDO;
import org.base.common.appexception.AppErrorCodeEnum;
import org.base.common.appexception.AppException;
import org.base.common.constans.SystemConstant;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

@Component
public class SelectMenuManagerImpl implements SelectMenuManager {
    @Resource
    private SysMenuDao sysMenuDao;
    @Resource
    private SysRoleMenuDao sysRoleMenuDao;
    @Resource
    private SysRoleDao sysRoleDao;
    @Resource
    private SysUserRoleDao sysUserRoleDao;

    @Override
    public SysMenuBO selectMenuById(Long sysMenuId) throws AppException {
        if (sysMenuId == null) {
            return null;
        }
       return SysMenuBeanUtil.sysMenuDOToBO(sysMenuDao.findById(sysMenuId));
    }

    @Override
    public List<SysMenuBO> selectMenuList(SysMenuQTO qto) throws AppException {
        qto.setNeedPaging(false);
        //如果不是超级管理员则仅查询用户角色所拥有的菜单
        if (qto.getSysUserId() != null && !SysUserBO.isAdmin(qto.getSysUserId())) {
            SysUserRoleDO relation = sysUserRoleDao.findByUserId(qto.getSysUserId());
            List<Long> menuIds = sysRoleMenuDao.findMenuIdsByRoleId(relation.getSysRoleId());
            qto.setSysMenuIds(menuIds);
        }

        List<SysMenuBO> menus = SysMenuBeanUtil.doList2BOList(sysMenuDao.findForPage(qto));
        //校验列表数据完整性，补充缺少的上层数据
        getMenuParent(menus);

        if (qto.getQueryDropdown()) {
            return buildMenuTree(menus.stream().filter(m -> !SystemConstant.F.equals(m.getMenuType())).collect(Collectors.toList()));
        }

        return buildMenuTree(menus);
    }

    /**
     * 校验列表数据完整性，补充缺少的上层数据
     * 逻辑：便利初始的数据列表，将不存在的父级菜单id保存在set集合中
     * 利用set去重的特点排除重复id
     * @param menus
     */
    private void getMenuParent(List<SysMenuBO> menus) {
        if (CollectionUtils.isEmpty(menus)) {
            return;
        }
        List<Long> idList = new ArrayList<>(menus.stream().map(SysMenuBO::getSysMenuId).collect(Collectors.toSet()));
        Set<Long> setIds = new HashSet<>();
        for (SysMenuBO menu : menus) {
            if (menu.getParentId().longValue() == 0L) {
                continue;
            }
            String[] strIds = menu.getAncestors().split(",");
            for (String id : strIds) {
                //如果是1级菜单，或者已存在列表中
                if (SystemConstant.STR_0.equals(id) || idList.contains(Long.parseLong(id))) {
                    continue;
                }
                setIds.add(Long.parseLong(id));
            }
        }
        if (setIds.size() > 0) {
            List<SysMenuDO> menuDOList = sysMenuDao.findMenuByIds(new ArrayList<>(setIds));
            menus.addAll(SysMenuBeanUtil.doList2BOList(menuDOList));
        }
    }

    @Override
    public List<SysMenuBO> selectMenuListByUserId(Long sysUserId) throws AppException {
        SysUserRoleDO relation = sysUserRoleDao.findByUserId(sysUserId);
        return selectMenuListByRoleId(relation.getSysRoleId());
    }

    @Override
    public List<SysMenuBO> selectMenuListByRoleId(Long sysRoleId) throws AppException {
        SysRoleDO roleDO = sysRoleDao.findById(sysRoleId);
        if (roleDO == null) {
            throw new AppException(AppErrorCodeEnum.UPDATE_DATE_NOT_EXISTS);
        }
        //如果是超级管理员则返回所有菜单
        if (SystemConstant.SUPER_ADMIN.equals(roleDO.getRoleKey())) {
            return SysMenuBeanUtil.doList2BOList(sysMenuDao.findAll());
        }
        List<Long> menuIds = sysRoleMenuDao.findMenuIdsByRoleId(sysRoleId);
        if (CollectionUtils.isEmpty(menuIds)) {
            return new ArrayList<>();
        }
        List<SysMenuBO> menus = SysMenuBeanUtil.doList2BOList(sysMenuDao.findMenuByIds(menuIds));

        return menus;
    }

    @Override
    public List<SysMenuBO> buildMenuTree(List<SysMenuBO> menus) throws AppException {
        if (CollectionUtils.isEmpty(menus)) {
            return new ArrayList<>();
        }
        List<SysMenuBO> result = menus.stream().filter(m -> "M".equals(m.getMenuType())).collect(Collectors.toList());
        //按照父级id分组
        Map<Long, List<SysMenuBO>> map = menus.stream().filter(m -> !"M".equals(m.getMenuType()))
                .collect(Collectors.groupingBy(SysMenuBO::getParentId));
        //递归获取子集
        getChildren(result, map);
        return result;
    }

    /**
     * 递归方法，获取菜单的子集
     * @param menus
     * @param map
     */
    private void getChildren(List<SysMenuBO> menus, Map<Long, List<SysMenuBO>> map) {
        for (SysMenuBO menu : menus) {
            if (!map.containsKey(menu.getSysMenuId())) {
                continue;
            }
            List<SysMenuBO> children = map.get(menu.getSysMenuId());
            menu.setChildren(children);
            getChildren(children, map);
        }
    }

    @Override
    public List<TreeSelect> buildTreeSelect(List<SysMenuBO> menus) throws AppException {
        List<SysMenuBO> menuTree = buildMenuTree(menus);
        List<TreeSelect> tree = menuTree.stream().map(TreeSelect::new).collect(Collectors.toList());
        return tree;
    }

    @Override
    public List<RouterVo> getRouters(List<SysMenuBO> menuTree) throws AppException {
        List<RouterVo> routers = new LinkedList<RouterVo>();
        for (SysMenuBO menu : menuTree) {
            RouterVo router = new RouterVo();
            router.setHidden(SystemConstant.STR_1.equals(menu.getVisible()));
            router.setName(SystemConstant.STR_1.equals(menu.getIsFrame()) && SystemConstant.M.equals(menu.getMenuType())? "": StringUtils.capitalize(menu.getPath()));
            router.setPath(RouterVoBeanUtil.getRouterPath(menu));
            router.setComponent(RouterVoBeanUtil.getComponent(menu));
            router.setQuery(menu.getQuery());
            router.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), SystemConstant.STR_1.equals(menu.getIsCache()), menu.getPath()));
            List<SysMenuBO> cMenus = menu.getChildren();
            if (CollectionUtils.isNotEmpty(cMenus) && SystemConstant.M.equals(menu.getMenuType())) {
                router.setAlwaysShow(true);
                router.setRedirect("noRedirect");
                router.setChildren(getRouters(cMenus));
            } else if (RouterVoBeanUtil.isMenuFrame(menu)) {
                router.setMeta(null);
                List<RouterVo> childrenList = new ArrayList<RouterVo>();
                RouterVo children = new RouterVo();
                children.setPath(menu.getPath());
                children.setComponent(menu.getComponent());
                children.setName(StringUtils.capitalize(menu.getPath()));
                children.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), SystemConstant.STR_1.equals(menu.getIsCache()), menu.getPath()));
                children.setQuery(menu.getQuery());
                childrenList.add(children);
                router.setChildren(childrenList);
            } else if (menu.getParentId().intValue() == 0 && RouterVoBeanUtil.isInnerLink(menu)) {
                router.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon()));
                router.setPath("/");
                List<RouterVo> childrenList = new ArrayList<RouterVo>();
                RouterVo children = new RouterVo();
                String routerPath = RouterVoBeanUtil.innerLinkReplaceEach(menu.getPath());
                children.setPath(routerPath);
                children.setComponent(SystemConstant.INNER_LINK);
                children.setName(StringUtils.capitalize(routerPath));
                children.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), menu.getPath()));
                childrenList.add(children);
                router.setChildren(childrenList);
            }
            routers.add(router);
        }
        return routers;

    }
}
